package com.lottery.system.service.impl;
import com.lottery.system.common.errorcode.ServiceErrorCodeConstants;
import com.lottery.system.common.exception.ServiceException;
import com.lottery.system.common.utils.JacksonUtil;
import com.lottery.system.common.utils.RedisUtil;
import com.lottery.system.controller.param.CreateActivityParam;
import com.lottery.system.controller.param.CreatePrizeActivityParam;
import com.lottery.system.controller.param.CreateUserActivityParam;
import com.lottery.system.controller.param.PageParam;
import com.lottery.system.dao.dataobject.ActivityDO;
import com.lottery.system.dao.dataobject.ActivityPrizeDO;
import com.lottery.system.dao.dataobject.ActivityUserDO;
import com.lottery.system.dao.dataobject.PrizeDO;
import com.lottery.system.dao.mapper.*;
import com.lottery.system.service.ActivityService;
import com.lottery.system.service.dto.ActivityDetailDTO;
import com.lottery.system.service.dto.ActivityInfoDTO;
import com.lottery.system.service.dto.CreateActivityDTO;
import com.lottery.system.service.dto.PageListDTO;
import com.lottery.system.service.enums.ActivityPrizeStatusEnum;
import com.lottery.system.service.enums.ActivityPrizeTiersEnum;
import com.lottery.system.service.enums.ActivityStatusEnum;
import com.lottery.system.service.enums.ActivityUserStatusEnum;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import java.util.List;
import java.util.Optional;

/**
 * @author: xiaoxie
 * create: 2024-09-09 10:30
 * @BelongsProject: lottery-system
 * @BelongsPackage: com.lottery.system.service.impl
 * description: 活动业务逻辑层接口的实现类
 */
@Service
public class ActivityServiceImpl implements ActivityService {
    /**
     * 活动缓存前置，为了区分业务
     */
    private final String ACTIVITY_PREFIX = "ACTIVITY_";
    /**
     * 活动缓存过期时间 3天
     */
    private final Long ACTIVITY_TIMEOUT = 60 * 60 * 24 * 3L;
    private final static Logger logger = LoggerFactory.getLogger(ActivityServiceImpl.class);
    @Autowired
    private ActivityMapper activityMapper;
    @Autowired
    private PrizeMapper prizeMapper;
    @Autowired
    private UserMapper userMapper;
    @Autowired
    private ActivityPrizeMapper activityPrizeMapper;
    @Autowired
    private ActivityUserMapper activityUserMapper;
    @Autowired
    private RedisUtil redisUtil;
    /**
     * @description: 创建活动
     * @author: xiaoxie
     * @date: 2024/9/9 10:31
     * @param: [param]
     * @return: com.lottery.system.service.dto.CreateActivityDTO
     **/
    @Override
    @Transactional
    public CreateActivityDTO createActivity(CreateActivityParam param) {

         // 检查参数
        checkActivityParam(param);
        //插入活动表
        ActivityDO activityDO = new ActivityDO();
        activityDO.setActivityName(param.getActivityName());
        activityDO.setDescription(param.getDescription());
        activityDO.setStatus(ActivityStatusEnum.RUNNING.name());
        activityMapper.insert(activityDO);
        //插入活动奖品表
        List<ActivityPrizeDO> activityPrizeDOList = param.getActivityPrizeList().stream()
                .map(prizeParam ->{
                    ActivityPrizeDO activityPrizeDO = new ActivityPrizeDO();
                    activityPrizeDO.setActivityId(activityDO.getId());
                    activityPrizeDO.setPrizeAmount(prizeParam.getPrizeAmount());
                    activityPrizeDO.setPrizeId(prizeParam.getPrizeId());
                    activityPrizeDO.setPrizeTiers(prizeParam.getPrizeTiers());
                    activityPrizeDO.setStatus(ActivityPrizeStatusEnum.INIT.name());
                    return activityPrizeDO;
                }).toList();
        activityPrizeMapper.batchInsert(activityPrizeDOList);
        //插入活动人员表
        List<ActivityUserDO> activityUserDOList = param.getActivityUserList().stream()
                .map(userParam ->{
                    ActivityUserDO activityUserDO = new ActivityUserDO();
                    activityUserDO.setUserName(userParam.getUserName());
                    activityUserDO.setActivityId(activityDO.getId());
                    activityUserDO.setUserId(userParam.getUserId());
                    activityUserDO.setStatus(ActivityUserStatusEnum.INIT.name());
                    return activityUserDO;
                }).toList();
        activityUserMapper.bachInsert(activityUserDOList);
        //整合数据 活动 + 人 + 奖品
        //获取奖品的完整信息
        List<Long> prizeIds = param.getActivityPrizeList().stream()
                .map(CreatePrizeActivityParam::getPrizeId)
                .distinct()
                .toList();
        List<PrizeDO> prizeDOList = prizeMapper.selectListByIds(prizeIds);
        //再整合
       ActivityDetailDTO activityDetailDTO  = integrateActivityInfo(activityDO,activityPrizeDOList
               ,activityUserDOList,prizeDOList);
        //存入redis
        cacheActivityDetailDTO(activityDetailDTO);
        //构造返回
        CreateActivityDTO activityDTO = new CreateActivityDTO();
        activityDTO.setActivityId(activityDO.getId());
        return activityDTO;
    }
    /**
     * @description: 获取活动列表
     * @author: xiaoxie
     * @date: 2024/9/9 22:07
     * @param: [param]
     * @return: com.lottery.system.service.dto.ActivityInfoDTO
     **/

    @Override
    public PageListDTO<ActivityInfoDTO> getActivityList(PageParam param) {
        //获取活动列表总数
        int total = activityMapper.count();
        //获取活动列表
        List<ActivityDO> activityDOList = activityMapper.selectActivityList(param.getOffSet(),param.getPageSize());
        List<ActivityInfoDTO> activityInfoDTOList = activityDOList.stream()
                                                    .map(aDO->{
                                                        ActivityInfoDTO activityInfoDTO = new ActivityInfoDTO();
                                                        activityInfoDTO.setActivityId(aDO.getId());
                                                        activityInfoDTO.setActivityName(aDO.getActivityName());
                                                        activityInfoDTO.setDescription(aDO.getDescription());
                                                        activityInfoDTO.setValid(aDO.valid());
                                                        return activityInfoDTO;
                                                    }).toList();
        //构造返回
        return new PageListDTO<ActivityInfoDTO>(total,activityInfoDTOList);
    }


    /**
     * @description: 获取活动详细信息
     * @author: xiaoxie
     * @date: 2024/9/10 16:11
     * @param: [activityId]
     * @return: com.lottery.system.service.dto.ActivityDetailDTO
     **/
    @Override
    public ActivityDetailDTO getActivityDetail(Long activityId) {
        //验证id是否为空
        if(activityId == null) {
            logger.info("activityId is null");
            return null;
        }
        ActivityDetailDTO detailDTO;
        //查redis
        detailDTO = getActivityFromCache(activityId);
        if(detailDTO != null) {
            return detailDTO;
        }
        //如果redis没有或者是已过期就查询数据库
        //detailDTO == null
        //查询活动表
        ActivityDO activityDO = activityMapper.selectActivityById(activityId);
        //查询活动奖品表
        List<ActivityPrizeDO> activityPrizeDOList = activityPrizeMapper.selectActivityPrizeById(activityId);
        //查询活动人员表
        List<ActivityUserDO> activityUserDOList = activityUserMapper.selectActivityUserById(activityId);
        //根据奖品id查询奖品表
        List<Long> prizeIds = activityPrizeDOList
                              .stream()
                              .mapToLong(ActivityPrizeDO::getPrizeId)
                              .distinct().boxed().toList();
        List<PrizeDO> prizeDOList = prizeMapper.selectListByIds(prizeIds);
        //整合数据
        detailDTO = integrateActivityInfo(activityDO,activityPrizeDOList
                                                         ,activityUserDOList,prizeDOList);
        //存储到redis中
        cacheActivityDetailDTO(detailDTO);
        //返回
        return detailDTO;
    }

    /**
     * @description: 缓存中奖记录
     * @author: xiaoxie
     * @date: 2024/9/13 17:32
     * @param: [activityId]
     * @return: void
     **/
    @Override
    public void cacheWinningInfo(Long activityId) {
        //验证id是否为空
        if(activityId == null) {
            logger.info("cacheWinningInfo: activityId is null");
            return;
        }
        ActivityDetailDTO detailDTO;
        //查询活动表
        ActivityDO activityDO = activityMapper.selectActivityById(activityId);
        //查询活动奖品表
        List<ActivityPrizeDO> activityPrizeDOList = activityPrizeMapper.selectActivityPrizeById(activityId);
        //查询活动人员表
        List<ActivityUserDO> activityUserDOList = activityUserMapper.selectActivityUserById(activityId);
        //根据奖品id查询奖品表
        List<Long> prizeIds = activityPrizeDOList
                .stream()
                .mapToLong(ActivityPrizeDO::getPrizeId)
                .distinct().boxed().toList();
        List<PrizeDO> prizeDOList = prizeMapper.selectListByIds(prizeIds);
        //整合数据
        detailDTO = integrateActivityInfo(activityDO,activityPrizeDOList
                ,activityUserDOList,prizeDOList);
        //存储到redis中
        cacheActivityDetailDTO(detailDTO);
    }

    /**
     * @description: 将活动的详细详细存储在redis中
     * @author: xiaoxie
     * @date: 2024/9/9 20:01
     * @param: [activityDetailDTO]
     * @return: void
     **/
    private void cacheActivityDetailDTO(ActivityDetailDTO activityDetailDTO) {
        if(null == activityDetailDTO ||null == activityDetailDTO.getActivityId()) {
            logger.warn("要缓存的活动信息不存在!");
            return;
        }
        try{
            redisUtil.setTimeout(ACTIVITY_PREFIX+activityDetailDTO.getActivityId()
                                      ,JacksonUtil.writeValueAsString(activityDetailDTO)
                                       ,ACTIVITY_TIMEOUT);
        }catch(Exception e){
            logger.error("缓存活动异常，ActivityDetailDTO={}",
                    JacksonUtil.writeValueAsString(activityDetailDTO),
                    e);
        }
    }
    /**
     * @description: 根据活动id查询redis的存储的活动具体数据
     * @author: xiaoxie
     * @date: 2024/9/9 20:07
     * @param: [activityId]
     * @return: com.lottery.system.service.dto.ActivityDetailDTO
     **/
    private ActivityDetailDTO getActivityFromCache(Long activityId) {
        if (null == activityId) {
            logger.warn("获取缓存活动数据的activityId为空！");
            return null;
        }
        try {
            String str = redisUtil.get(ACTIVITY_PREFIX + activityId);
            if (!StringUtils.hasText(str)) {
                logger.info("获取的缓存活动数据为空！key={}", ACTIVITY_PREFIX + activityId);
                return null;
            }
            return JacksonUtil.readValue(str, ActivityDetailDTO.class);
        } catch (Exception e) {
            logger.error("从缓存中获取活动信息异常，key={}", ACTIVITY_PREFIX + activityId, e);
            return null;
        }
    }
    /**
     * @description: 整合数据
     * @author: xiaoxie
     * @date: 2024/9/9 19:43
     * @param: [activityDO, activityPrizeDOList, activityUserDOList, prizeDOList]
     * @return: com.lottery.system.service.dto.ActivityDetailDTO
     **/

    private ActivityDetailDTO integrateActivityInfo(ActivityDO activityDO,
                                                    List<ActivityPrizeDO> activityPrizeDOList,
                                                    List<ActivityUserDO> activityUserDOList,
                                                    List<PrizeDO> prizeDOList) {
        ActivityDetailDTO activityDetailDTO = new ActivityDetailDTO();
        activityDetailDTO.setActivityId(activityDO.getId());
        activityDetailDTO.setDescription(activityDO.getDescription());
        activityDetailDTO.setActivityName(activityDO.getActivityName());
        activityDetailDTO.setActivityStatus(ActivityStatusEnum.forName(activityDO.getStatus()));
        //活动奖品列表
        List<ActivityDetailDTO.ActivityPrizeDTO> prizeDTOList = activityPrizeDOList
                .stream()
                .map(apDO -> {
                    ActivityDetailDTO.ActivityPrizeDTO prizeDTO = new ActivityDetailDTO.ActivityPrizeDTO();
                    prizeDTO.setPrizeId(apDO.getPrizeId());
                    //过滤掉相同奖品信息
                    Optional<PrizeDO> optionalPrizeDO = prizeDOList.stream()
                            .filter(prizeDO -> prizeDO.getId().equals(apDO.getPrizeId()))
                            .findFirst();
                    // 如果PrizeDO为空，不执行当前方法，不为空才执行
                    optionalPrizeDO.ifPresent(prizeDO -> {
                        prizeDTO.setName(prizeDO.getName());
                        prizeDTO.setImageUrl(prizeDO.getImageUrl());
                        prizeDTO.setPrice(prizeDO.getPrice());
                        prizeDTO.setDescription(prizeDO.getDescription());
                    });
                    prizeDTO.setPrizeTiers(ActivityPrizeTiersEnum.forName(apDO.getPrizeTiers()));
                    prizeDTO.setPrizeAmount(apDO.getPrizeAmount());
                    prizeDTO.setStatus(ActivityPrizeStatusEnum.forName(apDO.getStatus()));
                    return prizeDTO;
                }).toList();
        activityDetailDTO.setActivityPrizeDTO(prizeDTOList);
        //活动人员列表
        List<ActivityDetailDTO.ActivityUserDTO> userDTOList = activityUserDOList.stream()
                .map(userDO ->{
                    ActivityDetailDTO.ActivityUserDTO activityUserDTO = new ActivityDetailDTO.ActivityUserDTO();
                    activityUserDTO.setUserName(userDO.getUserName());
                    activityUserDTO.setUserId(userDO.getUserId());
                    activityUserDTO.setStatus(ActivityUserStatusEnum.forName(userDO.getStatus()));
                    return activityUserDTO;
                }).toList();
        activityDetailDTO.setActivityUserDTO(userDTOList);
        //返回
        return activityDetailDTO;
    }
    /**
     * @description: 校验参数
     * @author: xiaoxie
     * @date: 2024/9/9 19:43
     * @param: [param]
     * @return: void
     **/
    private void checkActivityParam(CreateActivityParam param) {
        logger.info("checkActivityParam param={}",param);
        //是否为空,多加这一步解耦合
        if(null == param) {
            logger.error(ServiceErrorCodeConstants.PARAM_IS_EMPTY.getMsg());
            throw new ServiceException(ServiceErrorCodeConstants.PARAM_IS_EMPTY);
        }
        //查询人员id是否存在
        List<Long> userIds = param.getActivityUserList().stream()
                .map(CreateUserActivityParam::getUserId)
                .distinct()
                .toList();
        List<Long> exitsIds = userMapper.exitsIds(userIds);
        if(exitsIds.isEmpty() || userIds.isEmpty()) {
            logger.error(ServiceErrorCodeConstants.ACTIVITY_USER_IS_ERROR .getMsg());
            throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_USER_IS_ERROR);
        }
        userIds.forEach(id -> {
            if (!exitsIds.contains(id)) {
                logger.error(ServiceErrorCodeConstants.ACTIVITY_USER_IS_ERROR.getMsg());
                throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_USER_IS_ERROR);
            }
        });
        //查询奖品id是否存在
        List<Long> prizeIds = param.getActivityPrizeList().stream()
                .map(CreatePrizeActivityParam::getPrizeId)
                .distinct()
                .toList();
        List<Long> exitsPrizeIds = prizeMapper.exitsPrizeIds(prizeIds);
        if(exitsPrizeIds.isEmpty() || prizeIds.isEmpty()) {
            logger.error(ServiceErrorCodeConstants.ACTIVITY_PRIZE_IS_ERROR.getMsg());
            throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_PRIZE_IS_ERROR);
        }
        prizeIds.forEach(id -> {
            if (!exitsPrizeIds.contains(id)) {
                logger.error(ServiceErrorCodeConstants.ACTIVITY_PRIZE_IS_ERROR.getMsg());
                throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_PRIZE_IS_ERROR);
            }
        });
        //人员数量需要大于等于奖品数量
        long userAmount = param.getActivityUserList().size();
        long prizeAmount = param.getActivityPrizeList().stream()
                .mapToLong(CreatePrizeActivityParam::getPrizeAmount)
                .sum();
        if(userAmount < prizeAmount) {
            logger.warn(ServiceErrorCodeConstants.ACTIVITY_PRIZE_OR_USER_IS_ERROR.getMsg());
            throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_PRIZE_OR_USER_IS_ERROR);
        }
        // 校验活动奖品等奖有效性
        param.getActivityPrizeList().forEach(prize -> {
            if (null == ActivityPrizeTiersEnum.forName(prize.getPrizeTiers())) {
                logger.error(ServiceErrorCodeConstants.ACTIVITY_PRIZE_IS_ERROR.getMsg());
                throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_PRIZE_TIERS_ERROR);
            }
        });

    }
}
